2.7 自定义类型

使用关键字type定义用户自定义类型,包括基于现有基础类型创建,或者是结构体、函数类型等

type flags byte
  
const( 
   read flags=1<<iota
   write
   exec
) 
  
func main() { 
   f:=read|exec
   fmt.Printf("%b\n",f)     // 输出二进制标记位 
}
 

输出:

101

和var、const类似,多个type定义可合并成组,可在函数或代码块内定义局部类型。

func main() { 
   type(              // 组 
       user struct{          // 结构体 
           name string
           age uint8
        } 
  
       event func(string)bool    // 函数类型 
    ) 
  
   u:=user{"Tom",20} 
   fmt.Println(u) 
  
   var f event=func(s string)bool{ 
       println(s) 
       return s!= "" 
    } 
  
   f("abc") 
}

即便指定了基础类型,也只表明它们有相同底层数据结构,两者间不存在任何关系,属完全不同的两种类型。除操作符外,自定义类型不会继承基础类型的其他信息(包括方法)。不能视作别名,不能隐式转换,不能直接用于比较表达式。

func main() { 
   type data int
   var d data=10
  
   var x int=d       // 错误:cannot use d(type data)as type int in assignment
   println(x) 
  
   println(d==x)      // 错误:invalid operation:d==x(mismatched types data and int) 
}
 

未命名类型

与有明确标识符的bool、int、string等类型相比,数组、切片、字典、通道等类型与具体元素类型或长度等属性有关,故称作未命名类型(unnamed type)。当然,可用type为其提供具体名称,将其改变为命名类型(named type)。

具有相同声明的未命名类型被视作同一类型。

  • 具有相同基类型的指针。
  • 具有相同元素类型和长度的数组(array)。
  • 具有相同元素类型的切片(slice)。
  • 具有相同键值类型的字典(map)。
  • 具有相同数据类型及操作方向的通道(channel)。
  • 具有相同字段序列(字段名、字段类型、标签,以及字段顺序)的结构体(struct)。
  • 具有相同签名(参数和返回值列表,不包括参数名)的函数(func)。
  • 具有相同方法集(方法名、方法签名,不包括顺序)的接口(interface)。

相关类型会在后续章节做详细说明,此处无须了解更多细节。

容易被忽视的是struct tag,它也属于类型组成部分,而不仅仅是元数据描述。

func main() { 
   var a struct{   // 匿名结构类型 
       x int    `x` 
       s string `s` 
    } 
  
   var b struct{ 
       x int
       s string
    } 
  
   b=a      // 错误:cannot use a type
               //      struct{x int"x";s string"s" }as type
               //      struct{x int;s string}in assignment
  
   fmt.Println(b) 
}

同样,函数的参数顺序也属签名组成部分。

func main() { 
   var a func(int,string) 
   var b func(string,int) 
  
   b=a      // 错误:cannot use a(type func(int,string))as type
         //      func(string,int)in assignment
   b("s",1) 
}

未命名类型转换规则:

  • 所属类型相同。
  • 基础类型相同,且其中一个是未命名类型。
  • 数据类型相同,将双向通道赋值给单向通道,且其中一个为未命名类型。
  • 将默认值nil赋值给切片、字典、通道、指针、函数或接口。
  • 对象实现了目标接口。
func main() { 
   type data[2]int
   var d data= [2]int{1,2}  // 基础类型相同,右值为未命名类型 
  
   fmt.Println(d) 
  
   a:=make(chan int,2) 
   var b chan<-int=a // 双向通道转换为单向通道,其中b为未命名类型 
  
   b<-2
}